
package com.chasekernan.hxnova.client.plugins;

#if flash9

import com.chasekernan.hxnova.client.Selection;
import com.chasekernan.hxnova.client.SelectionFilter;
import flash.utils.Timer;
import flash.events.TimerEvent;
import swhx.Connection;
import hscript.Parser;
import fscript.FInterp;
import com.chasekernan.hxnova.client.plugins.PluginApi;
import com.chasekernan.hxnova.client.ClientBase;
import com.chasekernan.hxnova.core.dataholders.Global;
import com.chasekernan.hxnova.utils.Translator;
import hscript.Interp;

class PluginManager {
    
    
    private static var INSTANCE     : PluginManager;
    
    private var plugins             : Hash<Plugin>;
    private var pluginVariables     : Hash<Dynamic>;
    
    
    public static function getInstance() : PluginManager {
        if (INSTANCE == null) {
            INSTANCE = new PluginManager();
            
            //initialize a few other classes
            PluginApi.getInstance();
        }
        
        return INSTANCE;
    }
    
    private function new() {
        plugins         = new Hash();
        pluginVariables = new Hash();
        
        setupPluginVariables();
    }
    
    private function setupPluginVariables() {
        pluginVariables.set("c", 
            function(name : String) { 
                return Type.resolveClass(name); 
            } 
        );
        
        pluginVariables.set("n", 
            function(c : Class<Dynamic>, ?args : Array<Dynamic>) { 
                return Type.createInstance(c, if (args == null) [] else args); 
            } 
        );
        
        //takes args like:
        //    ["label", "test"],
        //    ["data", 5]
        //and returns {label : "test", data : 5}
        pluginVariables.set("o", Reflect.makeVarArgs(
            function(a : Array<Dynamic>) : Dynamic {
                var obj : Dynamic   = {};
                
                for (pair in a) {
                    Reflect.setField(obj, pair[0], pair[1]);
                }
                
                return obj;
            }
        ));
        
        pluginVariables.set("toXml", 
            function(obj : Dynamic) {
                return Xml.parse(obj.toXMLString()).firstElement();
            }
        );
        
        
        pluginVariables.set("PluginManager",    this);
        pluginVariables.set("Lib",              flash.Lib);
        
        var connection  = ClientBase.getInstance().getConnection();
        pluginVariables.set("swhxConnection",   connection);
        
        pluginVariables.set("Global",           Global);
        pluginVariables.set("Translator",       Translator);
        pluginVariables.set("Type",             Type);
        pluginVariables.set("Reflect",          Reflect);
        pluginVariables.set("Math",             Math);
        pluginVariables.set("Selection",        Selection);
        pluginVariables.set("SelectionFilter",  SelectionFilter);
        pluginVariables.set("callLater",
            function(func : Dynamic) {
                var timer = new Timer(65);
                timer.addEventListener(TimerEvent.TIMER_COMPLETE, 
                    function(?e : Dynamic) {
                        func();
                    }
                );
            }
        );
        
    }
    
    public function getPlugins() : Hash<Plugin> {
        return plugins;
    }
    
    public function getPlugin(name : String) : Plugin {
        return plugins.get(name);
    }
    
    public function loadPlugin(name : String, onComplete : Plugin -> Void) {
        var connection  = ClientBase.getInstance().getConnection();
        var script      = connection.PluginFileManager.getPluginFile.call([name]);
        
        try {
			
            var interp  = #if HINTERP new Interp(); 
						  #else       new FInterp();
						  #end
						  
            for (v in pluginVariables.keys()) {
                interp.variables.set(v, pluginVariables.get(v));
            }
            
			var expr = new Parser().parseString(script);
			
			#if HINTERP
				onComplete(cast interp.execute(expr));
			#else
				interp.execute(expr, cast onComplete);
			#end
        } catch (e : Dynamic) {
            connection.SwhxBase.showMessage.call(["Error", 
                "Error while parsing plugin " + name + ": " + e]);
        }
    }
    
    public function addPluginVariable(name : String, value : Dynamic) {
        pluginVariables.set(name, value);
    }
    
    public function getPluginVariable(name : String) : Dynamic {
        return pluginVariables.get(name);
    }
    
    public function loadPlugins(?onComplete : Void -> Void) {
        var connection  = ClientBase.getInstance().getConnection();
        var loadOrder   = connection.PluginFileManager.getLoadOrder.call([]);
        
        var iter : Iterator<String> = cast loadOrder.iterator();
        
        if (iter.hasNext()) {
            var name    = iter.next();
            var me      = this;
            
            loadPlugin(name, 
                function(plugin) {
                    me.loadNextPlugin(name, plugin, iter, onComplete);
                }
            );
        }
    }
    
    public function destroyPlugins() {
        for (plugin in plugins) plugin.tearDown();
    }
    
    private function loadNextPlugin(name, plugin, iter, ?onComplete) {
        plugins.set(name, plugin);
        plugin.setup();
        
        if (!iter.hasNext()) {
            if (onComplete != null) onComplete();
            return;
        }
            
        name    = iter.next();
        var me  = this;
        loadPlugin(name, 
            function(plugin) {
                me.loadNextPlugin(name, plugin, iter, onComplete);
            }
        );
    }
}

#end